/******************************************************************************/
/* @ All Rights Reserved Copyright (C) 2015 broada,Ltd.                       */
/*                                                                            */
/*  NAME      = syncmodule_core.c                                             */
/*  NOTE      =                                                               */
/*  DATE      = 2015/03/05 by guojq                                           */
/******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <sys/time.h>

#include "frmem.h"
#include "frlog.h"
#include "frsutl.h"
#include "syncmodule_core.h"
#include "module.h"
#include "module_comm.h"
#include "frct_comm.h"
#include "frreport_comm.h"

typedef struct __st_L7session_syncmodule {
    unsigned long ul_seq;                              /* 序列号，1次req和response时，使用同一个序列号 */
    Frlist_header *p_lst_req_tmp;                       /* req信息列表 */
    Frlist_header *p_lst_comm_tmp;                      /* 共通信息 */
    //char arr_session_id[APM_UUID_SIZE];              /* session ID, UUID */
   L4_transaction st_l4_trans;                         /* 数据 */

}L7session_syncmodule;

/*******************************************************************************
*  FUNC     :  判断是否需要做syncmodule
*  ARGS     :  p_st_conn                    输入数据信息
*           :  p_st_data_in                 输入数据
*  RTN      :  APM_L7_IS_NO                 不做
*           :  APM_L7_IS_YES                做
*  NOTE     :
*******************************************************************************/
int syncmodule_detect(Conntrack *p_st_conn, Apm_packet *p_st_packet)
{

    //判断是否需要做
    return APM_L7_IS_YES;
}

/*******************************************************************************
*  FUNC     :  创建L7session_syncmodule
*  ARGS     :  void
*  RTN      :  NULL                         (失败)
*           :  p_st_syncmodule_private      (成功)
*  NOTE     :
*******************************************************************************/
FR_STATIC L7session_syncmodule* __syncmodule_private_create(void)
{
    L7session_syncmodule *p_st_syncmodule_private = NULL;

    //申请内存并初始化
    p_st_syncmodule_private = (L7session_syncmodule *)frmalloc(sizeof(L7session_syncmodule));
    if(p_st_syncmodule_private == NULL)
        return NULL;

    memset(p_st_syncmodule_private,'\0',sizeof(L7session_syncmodule));

    return p_st_syncmodule_private;

}

/*******************************************************************************
*  FUNC     :  私有信息销毁
*  ARGS     :  p_private_session(shortTCP的私有session数据)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
int syncmodule_session_destroy(void *p_private_session)
{
    L7session_syncmodule *p_st_private = (L7session_syncmodule *)p_private_session;

    if(p_st_private != NULL)
    {
        if(p_st_private->p_lst_comm_tmp)
            frreport_body_l7list_free(&p_st_private->p_lst_comm_tmp);

        if(p_st_private->p_lst_req_tmp)
            frreport_body_l7list_free(&p_st_private->p_lst_req_tmp);

        frfree(p_st_private);
    }

    return FR_SUCC;
}

int frtime_chk(struct timeval *p_tv_pre, struct timeval *p_tv_now)
{
    if (p_tv_now->tv_sec < p_tv_pre->tv_sec)
        return 0;
    if ((p_tv_now->tv_sec == p_tv_pre->tv_sec) && p_tv_now->tv_usec < p_tv_pre->tv_usec)
        return 0;
    return 1;
}

/*******************************************************************************
*  FUNC     :  syncmodule_data格式化
*  ARGS     :  p_st_conn                    p_st_conn(入力链接信息)
*           :  p_st_hash_report             p_st_hash_report(保存Frreport_data)
*           :  p_st_l4_private              L4
*           :  ul_seq                       seq
*  RTN      :  FR_FAIL                      (失败)
*           :  FR_SUCC                      (成功)
*  NOTE     :
*******************************************************************************/
FR_STATIC int syncmodule_data_format(Conntrack *p_st_conn
                        , Frhash_header *p_st_hash_report
                        , L4_transaction *p_st_l4_private
                        , unsigned long ul_seq
                        , L7session_syncmodule *p_st_private)
{
    int i_ret = FR_FAIL;
    Frtrans_info  st_trans_info;
    L4_transaction *p_st_l4_trans = NULL;
    int i_use_request = FR_NO;
    int i_use_response = FR_NO;
    Frreport_data *p_st_report_data = NULL;
    Frreport_body_data *p_st_body_data = NULL;
    unsigned long ul_tmp = 0;

    if(p_st_l4_private == NULL || p_st_conn == NULL || p_st_private == NULL)
        return FR_FAIL;

    memset(&st_trans_info,0x00,sizeof(Frtrans_info));
    p_st_l4_trans = &(st_trans_info.st_l4_trans);

    //设置p_st_l4_trans
    //st_response_time_first_byte
    memcpy(p_st_l4_trans,p_st_l4_private,sizeof(L4_transaction));

    if (p_st_l4_trans->st_request_time_first_byte.tv_sec ||
            p_st_l4_trans->st_request_time_first_byte.tv_usec)
    {
        //应答时间
        p_st_l4_trans->ul_response_time_us = (unsigned long)frtime_diff(&(p_st_l4_trans->st_request_time_first_byte)
                            , &(p_st_l4_trans->st_response_time));
        i_use_request = FR_YES;
    }

    if (p_st_l4_trans->st_response_time_first_byte.tv_sec ||
                p_st_l4_trans->st_response_time_first_byte.tv_usec)
        i_use_response = FR_YES;

    //创建数据链表
    p_st_body_data = frreport_body_new_l7(p_st_conn);
    if(p_st_body_data == NULL)
        return FR_FAIL;

    if(i_use_response)
    {
        //数据链表  设置为response
        if(i_use_request == FR_NO)
        {
            i_ret = frreport_body_l7_add_connInfo(p_st_body_data, p_st_conn);
            if(i_ret == FR_FAIL)
                goto ERROR;

        }
        else
        {
            //comm
            if(FRREPORT_BODY_L7LIST_SET_COMM(p_st_body_data, &p_st_private->p_lst_comm_tmp) == FR_FAIL)
                goto ERROR;

            //把req数据链表添加到resp中
            if(FRREPORT_BODY_L7LIST_SET_REQ(p_st_body_data, &p_st_private->p_lst_req_tmp) == FR_FAIL)
                goto ERROR;

            if (frtime_chk(&p_st_l4_private->st_request_time, &p_st_l4_private->st_response_time_first_byte) == 0)
            {
                FRLOG_WARN("syncmodule data error");
                goto ERROR;
            }
            ul_tmp = (unsigned long)frtime_diff(&p_st_l4_private->st_request_time, &p_st_l4_private->st_response_time_first_byte);
            if(frreport_body_l7_add_ulong(p_st_body_data, APM_REPORT_KEY_RESP_SERVER_TIME_US, ul_tmp) == FR_FAIL)
                goto ERROR;

        }

        if(frreport_body_l7_add_timeval(p_st_body_data, APM_REPORT_KEY_RESP_START_TIMESTAMP, &p_st_l4_private->st_response_time_first_byte) == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_timeval(p_st_body_data, APM_REPORT_KEY_RESP_END_TIMESTAMP, &p_st_l4_private->st_response_time) == FR_FAIL)
            goto ERROR;

        ul_tmp = (unsigned long)frtime_diff(&p_st_l4_private->st_response_time_first_byte, &p_st_l4_private->st_response_time);
        if(frreport_body_l7_add_ulong(p_st_body_data, APM_REPORT_KEY_RESP_DOWNLOAD_TIME_US, ul_tmp) == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_long(p_st_body_data, APM_REPORT_KEY_RESP_DATA_BYTES, p_st_l4_private->l_response_bytes) == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_long(p_st_body_data, APM_REPORT_KEY_RESP_CODE, 0) == FR_FAIL)
            goto ERROR;

        p_st_report_data = frreport_data_l7_new(FRREPORT_TYPE_L7DATA_RESP);
        if(p_st_report_data == NULL)
            goto ERROR;

        if(frreport_data_add_body(p_st_report_data, p_st_body_data) == FR_FAIL)
            goto ERROR;
        p_st_body_data = NULL;

        if(frreport_data_hash(p_st_hash_report, p_st_report_data) == FR_FAIL)
            goto ERROR;

        return FR_SUCC;
    }

    if(i_use_request)
    {
        //数据链表  设置为request
        i_ret = frreport_body_l7_add_connInfo(p_st_body_data, p_st_conn);
        if(i_ret == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_ulong(p_st_body_data, APM_REPORT_KEY_REQ_ID, ul_seq) == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_int(p_st_body_data, APM_REPORT_KEY_REQ_ISRESPREQUIRED, 1) == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_timeval(p_st_body_data, APM_REPORT_KEY_REQ_START_TIMESTAMP, &p_st_l4_private->st_request_time_first_byte) == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_timeval(p_st_body_data, APM_REPORT_KEY_REQ_END_TIMESTAMP, &p_st_l4_private->st_request_time) == FR_FAIL)
            goto ERROR;

        ul_tmp = (unsigned long)frtime_diff(&p_st_l4_private->st_request_time_first_byte, &p_st_l4_private->st_request_time);
        if(frreport_body_l7_add_ulong(p_st_body_data, APM_REPORT_KEY_REQ_SEND_TIME_US, ul_tmp) == FR_FAIL)
            goto ERROR;

        if(frreport_body_l7_add_long(p_st_body_data, APM_REPORT_KEY_REQ_DATA_BYTES, p_st_l4_private->l_request_bytes) == FR_FAIL)
            goto ERROR;

        p_st_report_data = frreport_data_l7_new(FRREPORT_TYPE_L7DATA_REQ);
        if(p_st_report_data == NULL)
            goto ERROR;

        p_st_private->p_lst_comm_tmp = FRREPORT_BODY_L7LIST_CLONE_COMM(p_st_body_data);
        if(p_st_private->p_lst_comm_tmp == NULL)
            goto ERROR;

        p_st_private->p_lst_req_tmp = FRREPORT_BODY_L7LIST_CLONE_REQ(p_st_body_data);
        if(p_st_private->p_lst_req_tmp == NULL)
            goto ERROR;

        if(frreport_data_add_body(p_st_report_data, p_st_body_data) == FR_FAIL)
            goto ERROR;
        p_st_body_data = NULL;

        if(frreport_data_hash(p_st_hash_report, p_st_report_data) == FR_FAIL)
            goto ERROR;

        return FR_SUCC;
    }

    return FR_SUCC;

ERROR:
    if(p_st_body_data)
        frreport_body_free(&p_st_body_data);

    if(p_st_report_data)
        frreport_data_free(&p_st_report_data);

    if(p_st_private->p_lst_comm_tmp)
        frreport_body_l7list_free(&p_st_private->p_lst_comm_tmp);

    if(p_st_private->p_lst_req_tmp)
        frreport_body_l7list_free(&p_st_private->p_lst_req_tmp);

    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  syncmodule的packet解析
*  ARGS     :  p_st_conn(入力链接信息)
            :  p_st_packet(入力packet信息)
            :  p_st_hash_report(保存Frreport_data)
*  RTN      :  FR_FAIL          (失败)
*           :  FR_SUCC          (成功)
*  NOTE     :
*******************************************************************************/
int syncmodule_analysis(Conntrack *p_st_conn
                      , Apm_packet *p_st_packet
                      , Frhash_header *p_st_hash_report)
{
    L7_session *p_st_session = NULL;                   /* L7session */
    L7session_syncmodule *p_st_private = NULL;
    L4_transaction *p_st_l4_trans = NULL;

    if(p_st_conn == NULL || p_st_packet==NULL || p_st_hash_report == NULL)
        return APM_L7_ANALYZE_FAIL;

    /* 到Conntrack中取得session信息 */
    p_st_session = frct_get_session(p_st_conn);
    if (p_st_session == NULL)
    {
        return APM_L7_ANALYZE_FAIL;
    }

    /*
    * 如果入力p_st_data_in是FIN包, 说明本次connection结束,
    * 释放本次session信息. 如果入力p_st_data_in是SYN包,说明新的连接
    * connection开始了, 释放上次可能残留的session信息
    */
    if (p_st_packet->em_analyz_cmd == FRANALYZ_CMD_FIN
        || p_st_packet->em_analyz_cmd == FRANALYZ_CMD_SYN)
    {
        if (p_st_session->p_session_private)
        {
            p_st_private = (L7session_syncmodule *)p_st_session->p_session_private;
            p_st_l4_trans = &(p_st_private->st_l4_trans);
            //有数据上报
            if (p_st_private->ul_seq == 0)
                p_st_private->ul_seq = frct_seq_increase(p_st_conn);
            syncmodule_data_format(p_st_conn, p_st_hash_report, p_st_l4_trans, p_st_private->ul_seq, p_st_private);
            /* 销毁所有相关本次的connection的数据 */
            syncmodule_session_destroy(p_st_session->p_session_private);
            p_st_session->p_session_private = NULL;
        }

        return APM_L7_ANALYZE_SUCC;
    }

    /* 本次链路的第一个有效packet包,创建链路 */
    if (p_st_session->p_session_private == NULL)
    {
        /* 初始化SQL Server的私有数据 */
        p_st_session->p_session_private = __syncmodule_private_create();
        if (p_st_session->p_session_private == NULL)
        {
            return APM_L7_ANALYZE_FAIL;
        }
        /* 设置session ID */
        fruuid(p_st_session->arr_session_id);
    }
    p_st_private = (L7session_syncmodule *) p_st_session->p_session_private;
    p_st_l4_trans = &(p_st_private->st_l4_trans);

    //request
    if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
    {
        //存在response则上一次同步连接已结束
        if (p_st_l4_trans->st_response_time_first_byte.tv_sec ||
                p_st_l4_trans->st_response_time_first_byte.tv_usec)
        {
            //上报上一次的同步数据
            if (p_st_private->ul_seq == 0)
                p_st_private->ul_seq = frct_seq_increase(p_st_conn);

            syncmodule_data_format(p_st_conn, p_st_hash_report, p_st_l4_trans, p_st_private->ul_seq, p_st_private);

            //重置同步数据
            memset(p_st_private, 0x00, sizeof(L7session_syncmodule));

            p_st_private = (L7session_syncmodule *) p_st_session->p_session_private;
            p_st_l4_trans = &(p_st_private->st_l4_trans);
        }
        //第一个包  st_request_time_first_byte
        if (p_st_l4_trans->st_request_time_first_byte.tv_sec == 0 &&
                p_st_l4_trans->st_request_time_first_byte.tv_usec == 0)
        {
            p_st_l4_trans->st_request_time_first_byte.tv_sec = p_st_packet->st_timestamp.tv_sec;
            p_st_l4_trans->st_request_time_first_byte.tv_usec = p_st_packet->st_timestamp.tv_usec;

            p_st_l4_trans->st_request_time_start.tv_sec = p_st_packet->st_timestamp.tv_sec;
            p_st_l4_trans->st_request_time_start.tv_usec = p_st_packet->st_timestamp.tv_usec;
        }

        p_st_l4_trans->st_request_time.tv_sec = p_st_packet->st_timestamp.tv_sec;
        p_st_l4_trans->st_request_time.tv_usec = p_st_packet->st_timestamp.tv_usec;
        /* 请求的字节数 */
        p_st_l4_trans->l_request_bytes += p_st_packet->ui_payload_len;
        return APM_L7_ANALYZE_SUCC;
    }

    //response
    if(p_st_packet->i_direction == FR_DIRECT_SRV2CLIENT)
    {
        if (p_st_l4_trans->st_response_time_first_byte.tv_sec == 0 &&
                p_st_l4_trans->st_response_time_first_byte.tv_usec == 0)
        {
            if (p_st_l4_trans->st_request_time_first_byte.tv_sec ||
                p_st_l4_trans->st_request_time_first_byte.tv_usec)
            {
                //上报request
                p_st_private->ul_seq = frct_seq_increase(p_st_conn);

                if (syncmodule_data_format(p_st_conn, p_st_hash_report, p_st_l4_trans, p_st_private->ul_seq, p_st_private) != FR_SUCC)
                {
                    //重置同步数据
                    memset(p_st_private, 0x00, sizeof(L7session_syncmodule));
                    return APM_L7_ANALYZE_FAIL;
                }
            }
            //应答发起时间戳
            p_st_l4_trans->st_response_time_first_byte.tv_sec = p_st_packet->st_timestamp.tv_sec;
            p_st_l4_trans->st_response_time_first_byte.tv_usec = p_st_packet->st_timestamp.tv_usec;
            
            p_st_l4_trans->st_response_time.tv_sec = p_st_packet->st_timestamp.tv_sec;
            p_st_l4_trans->st_response_time.tv_usec = p_st_packet->st_timestamp.tv_usec;
            
            if (p_st_l4_trans->st_request_time_first_byte.tv_sec ||
                    p_st_l4_trans->st_request_time_first_byte.tv_usec)
            {
                //首包时间
                p_st_l4_trans->ul_response_time_firstbyte_us = (unsigned long)frtime_diff(&(p_st_l4_trans->st_request_time_first_byte)
                                    , &(p_st_l4_trans->st_response_time_first_byte));
            }
        }
        p_st_l4_trans->st_response_time.tv_sec = p_st_packet->st_timestamp.tv_sec;
        p_st_l4_trans->st_response_time.tv_usec = p_st_packet->st_timestamp.tv_usec;
        
        p_st_l4_trans->l_response_bytes += p_st_packet->ui_payload_len;
        return APM_L7_ANALYZE_SUCC;
    }
    
    return APM_L7_ANALYZE_SUCC;
}

